package stream;

/**
 * 1. Stream 自己不会存储元素，
 * 2. stream 不会改变源数据。相反，他们会返回一个持有结果的新 stream
 * 3. stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
 */

import lambda.Employee;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;

/**
 * steam 的三个操作步骤
 * 1. 创建 stream
 * 2. 中间操作
 * 3. 终止操作（终端操作）
 */
public class TestStreamApi2 {
    List<Employee> employees = Arrays.asList(
            new Employee("张三",18,9999.99),
            new Employee("李四",58,5555.55),
            new Employee("王五",26,3333.33),
            new Employee("赵六",36,6666.66),
            new Employee("田七",12,8888.88),
            new Employee("田七",12,8888.88),
            new Employee("田七",12,8888.88)
    );
    // 中间操作

    /**
     * 排序
     * sorted() —— 自然排序（Comparable）
     * sorted() —— 定制排序（Comparator)
     */
    @Test
    public void test8(){
        List<String> list = Arrays.asList("ddd","ccc","aaa", "bbb","eee");
        list.stream()
                .sorted()
                .forEach(System.out::println);// 输出：aaa   bbb   ccc   ddd   eee
        System.out.println("--------------------------");

        employees.stream()
                .sorted((e1,e2) -> {
                    if (e1.getAge() == e2.getAge()){
                        return e1.getName().compareTo(e2.getName());
                    }else{
                        return -Integer.compare(e1.getAge(),e2.getAge());
                    }
                }).forEach(System.out::println);
    }

    /**
     * 映射
     * map —— 接收lambda，将元素转换为其他形式或者提取信息。接收一个函数作为参数，该函数会被应用到每个元素上，并将其映射成一个新元素。
     * flatMap —— 接收一个函数作为参数，将流中的每个值都换成另一个流，然后把所有流连接成一个流
     */
    @Test
    public void test7() {
        List<String> list = Arrays.asList("aaa", "bbb","ccc","ddd","eee");
        list.stream()
                .map((str) -> str.toUpperCase())
                .forEach((s -> System.out.println(s)));
//        System.out.println("-------------------------");
//        employees.stream()
//                .map(Employee::getName)
//                .forEach(System.out::println);
        System.out.println("-------------------------");

//        Stream<Stream<Character>> stream = list.stream()
//                .map(TestStreamApi2::filterCharacter);// 相当于是：{{a,a,a},{b,b,b},{c,c,c}}
//
//        stream.forEach((sm) -> {
//            sm.forEach(System.out::println);
//        });
        System.out.println("-------------------------");
        Stream<Character> stream1 = list.stream()
                .flatMap(TestStreamApi2::filterCharacter); // 相当于是：{a,a,a,b,b,b,c,c,c}
        stream1.forEach(System.out::println);
    }

    public static Stream<Character> filterCharacter(String str) {
        List<Character> list = new ArrayList<>();
        for (Character ch:str.toCharArray()) {
            list.add(ch);
        }
        return list.stream();
    }

    /**
     * 筛选与切片
     * filter —— 接收 lambda，从流中排除某些元素
     * limit —— 截断流，使其元素不超过给定数量
     * skip —— 跳过元素，返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个，则返回一个空流。与 limit(n) 互补
     * distinct —— 筛选，通过流所生成元素的 hashCode() 和 equals() 去处重复元素
     */

    @Test
    public void test6() {
        employees.stream()
                .filter((e) -> e.getSalary() > 5000)
                .skip(2) // 跳过前两条
                .distinct() // 需要有hashCode和equals方法（必须是实现了此两方法，在Person中有效，只在Employee中写无效）
                .forEach(System.out::println);
    }

    @Test
    public void test5() {
        employees.stream()
                .filter((e) -> e.getSalary() > 5000)
                .skip(2) // 跳过前两条
                .distinct() // 需要有hashCode和equals方法（必须是实现了此两方法，在Person中有效，只在Employee中写无效）
                .forEach(System.out::println);
    }

    @Test
    public void test4() {
        employees.stream()
                .filter((e) ->  {
                    System.out.println("短路");
                    return e.getSalary() > 5000;
                })
                .skip(2) // 跳过前两条
                .forEach(System.out::println);
    }
    @Test
    public void test3() {
        employees.stream()
                .filter((e) ->  {
                    System.out.println("短路");
                    return e.getSalary() > 5000;
                })
                .limit(2) // 截断，使其元素不超过 2 个。一旦发现了两条满足条件的元素后，就不再继续遍历了。这称为 “短路” 与 && || 两运算符的逻辑有点类似
                .forEach(System.out::println);
    }

    // 内部迭代，迭代操作由 stream api 完成的
    @Test
    public void test1() {
        // 中间操作，筛选。不会执行任何操作
        Stream<Employee> stream = employees.stream()
                .filter((e) -> {
                    System.out.println("Stream API 的中间操作"); // 没有终止操作的话，这句打印不会执行
                    return e.getAge() > 35;
                });
        // 终止操作：一次性执行全部内容。这个过程被称为 “惰性求值”
        stream.forEach(System.out::println);

    }

    // 外部迭代
    @Test
    public void test2() {
        Iterator<Employee> it = employees.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}
